using System.Collections.Generic;
using System.Linq;
using System;

namespace SolutionOrchestrator.Validators
{
	public class OutputPathValidator : XmlFileValidator
	{

		public static string Expected = @"$(SolutionDir)\_bin\$(AssemblyName)\$(Configuration)";

		public OutputPathValidator()
		{
			IsEnabled = false;
		}

		class OutputPathFix : Fix
		{
			public OutputPathFix(bool recommended = true) : base(recommended) {}

			public override bool Equals(object other)
			{
				return Equals(other as OutputPathFix);
			}

			public bool Equals(OutputPathFix other)
			{
				if (other == null)
				{
					return false;
				}
				return other.ToString() == ToString();
			}

			public override int GetHashCode()
			{
				return ToString().GetHashCode();
			}

			public override string ToString()
			{
				return Expected;
			}
		}

		class FixabelOutputPathViolation : FixableViolation
		{
			readonly XmlDoc _doc;

			public FixabelOutputPathViolation(XmlDoc doc)
			{
				_doc = doc;
			}

			protected override IEnumerable<Fix> FixesAvailableCore
			{
				get
				{
					yield return new OutputPathFix();
				}
			}

			protected override void ApplyFixCore(Fix fix)
			{
				bool changed = false;
				foreach (var node in _doc.Select("x:Project/x:PropertyGroup/x:OutputPath"))
				{
					if (node.InnerText != Expected)
					{
						changed = true;
						node.InnerText = Expected;
					}
				}
				if (changed)
				{
					_doc.Save();
				}
			}
		}

		protected override IEnumerable<Violation> Validate(XmlDoc doc, ValidationContext ctx)
		{
			foreach (var node in doc.Select("x:Project/x:PropertyGroup/x:OutputPath"))
			{
				var expected = ctx.SolutionDir;
				if (node.InnerText != Expected)
				{
					yield return new FixabelOutputPathViolation(doc)
					{
						Description = "Bad output path:" + node.InnerText,
					};
				}
			}
			bool any = false;
			foreach (var node in doc.Select("x:Project/x:PropertyGroup/x:IntermediatePath"))
			{
				any = true;
				if (node.InnerText != Expected)
				{
					yield return new Violation()
					{
						Description = "Bad intermediate path: " + node.InnerText,
					};
				}
			}
			if(!any)
			{
				yield return new Violation
				{
					Description = "Intermediate path not defined",
				};
			}
		}

	}
}